﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Environment.Hosting;
using System.Windows;

namespace Kokomo.Environment
{
	/// <summary>
	/// Represents a document editor.
	/// </summary>
	/// <remarks>
	/// Document editors provide services to the environment to load, save,
	/// and create documents that are exposed as a type derived from
	/// <see cref="DocumentViewModel"/>.  The environment only creates
	/// a single instance of the editor to handle all requests
	/// for file types associated with the editor.  An editor
	/// specifies its native file types using <see cref="SupportedFileTypes"/>.
	/// <para>
	/// Implementations can retrieve extensions using <see cref="Services.EnvironmentServices.GetExtensions"/>.
	/// </para>
	/// </remarks>
	/// <seealso cref="DocumentViewModel"/>
	public abstract class DocumentEditor : NotifierObject
	{
		/// <summary>
		/// Gets the title of the editor.
		/// </summary>
		public abstract string Title { get; }

		private FileType[] _fileTypes;
		/// <summary>
		/// Gets the list of <see cref="FileType"/>s describing the
		/// document types supported by the editor.
		/// </summary>
		/// <remarks>
		/// When the user opens a file, the environment searchs for
		/// the editor that supports a file type matching the file.
		/// </remarks>
		/// <seealso cref="FileType"/>
		public IEnumerable<FileType> SupportedFileTypes
		{
			get { return this._fileTypes ?? new FileType[0]; }
			protected set
			{
				if (value == null) this._fileTypes = new FileType[0];
				else
				{
					FileType[] fileTypeArray = value.ToArray();
					foreach (var fileType in fileTypeArray)
					{
						if (fileType == null) throw new ArgumentException("SupportedFileTypes cannon contain null");
					}
					this._fileTypes = fileTypeArray;
				}
			}
		}

		private IEnumerable<FileTypeFilter> _filters;
		/// <summary>
		/// Gets the filters associated with this editor.
		/// </summary>
		public IEnumerable<FileTypeFilter> Filters
		{
			get { return this._filters; }
			set
			{
				if (value == null)
					this._filters = new FileTypeFilter[0];
				else
					this._filters = value;
			}
		}

		private IEnumerable<DocumentTemplate> _templates;
		protected virtual IEnumerable<DocumentTemplate> LoadTemplates()
		{
			return null;
		}
		/// <summary>
		/// Gets the document templates provided by the editor.
		/// </summary>
		/// <returns>A list of <see cref="DocumentTemplate"/> objects.</returns>
		/// <seealso cref="LoadTemplates"/>
		public IEnumerable<DocumentTemplate> GetTemplates()
		{
			if (this._templates == null)
			{
				this._templates = this.LoadTemplates();
				if (this._templates == null)
					this._templates = new DocumentTemplate[0];
			}

			return this._templates;
		}

		/// <summary>
		/// Creates a new <see cref="DocumentViewModel"/>.
		/// </summary>
		/// <returns>The new <see cref="DocumentViewModel"/>, or <c>null</c>.</returns>
		/// <remarks>
		/// Implementations may display a UI to request more information
		/// from the user before creating the document.
		/// If the user cancels the operation, <c>null</c> is returned.
		/// </remarks>
		public abstract DocumentViewModel CreateNew();
		/// <summary>
		/// Loads a document from a file.
		/// </summary>
		/// <param name="fileName">The file name of the document to open.</param>
		/// <returns>The <see cref="DocumentViewModel"/> representing the document, or <c>null</c>.</returns>
		/// <exception cref="ArgumentNullException"><paramref name="fileName"/> is <c>null</c>.</exception>
		/// <remarks>
		/// Implementations may display a UI to request more information
		/// from the user before creating the document.
		/// If the user cancels the operation, <c>null</c> is returned.
		/// <para>
		/// The environment associates the returned <see cref="DocumentViewModel"/>
		/// with <paramref name="fileName"/>.  If the operation causes the creation
		/// of a document that is logically distinct from the document identified
		/// by <paramref name="fileName"/>, such as a project or list containing document,
		/// then create the <see cref="DocumentViewModel"/>,
		/// set <see cref="DocumentViewModel.Title"/>, call <see cref="Services.IDocumentManager.ShowDocument"/>
		/// to display the document, and return <c>null</c>.
		/// </para>
		/// </remarks>
		public abstract DocumentViewModel OpenExisting(string fileName);

	}
}
